#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <dirent.h>
#include <sys/signal.h>
#include <sys/wait.h>
#include <sys/msg.h>
#include <sys/ipc.h>
#include <sys/time.h>
#include <unistd.h>

#include "../../common/include/filesystem.h"
#include "../include/general.h"
#include "../include/commonMQ.h"
#include "../include/common.h"
#include "../../server/include/server.h"

extern int doLC;

void sndMQAns(int mqid, int mqtype, int code)
{
    struct mymsg sbuf;
    int buf_length;

    clrtext(sbuf.mtext, FILE_LEN);

    sprintf (sbuf.mtext, "%d", code);

    buf_length = strlen(sbuf.mtext);
    sbuf.mtype = mqtype;

    if (msgsnd(mqid, &sbuf, buf_length, IPC_NOWAIT) < 0)
    {
        printf ("%d, %d, %s, %d\n", mqid, (int)sbuf.mtype, sbuf.mtext, buf_length);
        perror("msgsnd");
        exit(1);
    }

    return;
}

int rcvMQAns(int mqid, int mqtype)
{
    struct mymsg msg;
    int rtrn;
    int ans = -1;

    clrtext(msg.mtext, FILE_LEN);
    msg.mtype = mqtype;

    while (1)
    {
        if ((rtrn = msgrcv(mqid, &msg, FILE_LEN, mqtype, IPC_NOWAIT)) == -1)
        {
            if(errno != ENOMSG)
            {
                printf("msgop: msgrcv failed");
                return -1;
            }
        }
        else
        {
            sscanf(msg.mtext, "%d", &ans);
            return ans;
        }
    }

}


int
send_data_MQ (int mqid, int mqtype, char * s_msg, int size)
{
    int n_size;
    struct mymsg sbuf;
    int buf_length;

    if (size < 0 || s_msg == NULL)
        return -1;

    clrtext(sbuf.mtext, FILE_LEN);

    strcpy(sbuf.mtext, s_msg);
    buf_length = size;
    sbuf.mtype = mqtype;


    if ((n_size = msgsnd(mqid, &sbuf, buf_length, IPC_NOWAIT)) < 0)
    {
        printf ("%d, %d, %s, %d\n", mqid, (int)sbuf.mtype, sbuf.mtext, buf_length);
        perror("msgsnd");
        return -2;
    }

    return 1;
}

int
rcv_data_MQ (int mqid, int mqtype, char * r_msg)
{
    struct mymsg msg;
    int rtrn;
    int i = 5;

    clrtext(msg.mtext, FILE_LEN);
    msg.mtype = mqtype;


    while (i--)
    {
        if ((rtrn = msgrcv(mqid, &msg, FILE_LEN, mqtype, IPC_NOWAIT)) == -1)
        {
            if(errno != ENOMSG)
                printf("msgop: msgrcv failed");
        }
        else
        {
            sprintf(r_msg, "%s",msg.mtext);
            return rtrn;
        }
        usleep(100);
    }

    return -1;
}

int
nrcv_data_MQ (int mqid, int mqtype, char * r_msg, int n)
{
    struct mymsg msg;
    int rtrn;
    int i = 0;

    msg.mtype = mqtype;
    clrtext(msg.mtext, FILE_LEN);


    while (1)
    {
        if ((rtrn = msgrcv(mqid, &msg, FILE_LEN, mqtype, IPC_NOWAIT)) == -1)
        {
            if(errno != ENOMSG)
                printf("msgop: msgrcv failed");
        }
        else if(rtrn > 0)
        {
            while(i < n)
            {
                r_msg[i] = msg.mtext[i];
                i++;
            }

            if(rtrn-n != 0)
                send_data_MQ (mqid, mqtype, msg.mtext + i, rtrn - n);
            return n;
        }
    }

}

int readsizeMQ (int mqid, int mqtype)
{
    char chsize[17];
    int i = 0;
    int size;
    char c, rtrn;

    rtrn = nrcv_data_MQ (mqid, mqtype, &c, 1);
    if (c == ',')
    	c = 0;
    else
    	chsize[i++] = c;

    while (c != ',')
    {
        if ((rtrn = nrcv_data_MQ (mqid, mqtype, &c, 1)) < 0)
            fatal ("msg read failed\n");
        chsize[i++] = c;
    }

    chsize[i-1] = 0;
    printf("size received: %s\n", chsize);
    sscanf (chsize, "%d", &size);

    return size;
}

int readcomMQ (int mqid, int mqtype)
{
    char chsize[MAX_COMMAND_LEN] = {0};
    int size, rtrn = 0, i = 0;
    char c = '\0';

    while(c != ',')
    {
        rtrn = nrcv_data_MQ (mqid, mqtype, &c, 1);
        chsize[i++] = c;
    }

    chsize[i-1] = 0;
    printf("command received: %s\n", chsize);
    sscanf (chsize, "%d", &size);

    return size;
}

int snd_file_MQ (int mqid, int cliID, int servID, char * fname, int isdir)
{
    unsigned int fsize;
    FILE * pfile;
    char buf[BUF_SIZE] = {0};
    int acum, nread, back, name_len, tot_len;
    char answ[MSGSIZE] = {0};
    char msg[MSGSIZE] = {0};
    int cant = 0;

    if (isDir(fname) == 1)
    {
        sprintf(msg, "%d,%s", -1, fname);
        name_len = strlen(msg);
        sprintf(answ, "%d,%d,%d,%s", SND_FILE, name_len, -1, fname);

        tot_len = strlen (answ);

        if ((send_data_MQ (mqid, cliID, answ, tot_len)) < 0)
        {
            fatal ("send dir failed\n");
            return -1;
        }

        if ((back = rcvMQAns(mqid, servID)) != COM_ACK)
        {
            fatal ("Error received\n");
            return -1;
        }

        return 1;
    }

    fsize = getFileSize(fname);


    if ((pfile = fopen(fname, "r")) == NULL)
        fatal ("file open failed\n");

    sprintf(msg, "%d,%s", fsize, fname);
    name_len = strlen(msg);
    sprintf(answ, "%d,%d,%s", SND_FILE, name_len, msg);

    tot_len = strlen (answ);

    if ((send_data_MQ (mqid, cliID, answ, tot_len)) < 0)
    {
        fatal ("send file failed\n");
        return -1;
    }

    if ((back = rcvMQAns(mqid, servID)) != COM_ACK)
    {
        fatal ("Error received\n");
    }

    acum = 0;
    while (acum < fsize)
    {
        if ((fsize - acum) > BUF_SIZE)
            cant = BUF_SIZE;
        else
            cant = (fsize - acum);

        nread = fread(buf, 1, cant, pfile);
        if (nread < BUF_SIZE && nread < (fsize - acum))
        {
                fatal ("File read failure\n");
        }

        acum += nread;

        do
        {
            if ((send_data_MQ (mqid, cliID, buf, nread)) < 0)
            {
                fatal ("send dir failed\n");
                return -1;
            }

            back = rcvMQAns(mqid, servID);

        }while (back != COM_ACK);
    }


    return 1;

}

int
walkDirMQ(char *path, char *relPath, int mqid, int cliID, int servID)
{
    DIR *dip;
    struct dirent *dit;
    int i = 0;
    char *ext;
    char currPath[FILE_LEN];
    char currRelPath[FILE_LEN];

    /*
    * Open a directory stream to argv[1] and make sure
        * it's a readable and valid (directory) */
    if ((dip = opendir(path)) == NULL)
    {
        perror("opendir");
        return 0;
    }

    /*
    * Read in the files from argv[1] and print */
    while ((dit = readdir(dip)) != NULL)
    {
        i++;
        sprintf(currPath, "%s/%s", path, dit->d_name);

        /*TODO: Si se le pasa un path roto es probable que muera,
        no se pone ahora porque toda la info sale de readdir
        No envia archivos ni dirs que empiecen con '.'*/
        if (isDir(currPath))
        {
            if ( 0 == strcmp(".", dit->d_name) ||
                 0 == strcmp("..", dit->d_name) )
                continue;

            sprintf(currRelPath, "%s/%s", relPath, dit->d_name);


            if (snd_file_MQ (mqid, cliID, servID, currPath, 1) < 0)
            {
                fatal ("Error sending file\n");
            }

            walkDirMQ(currPath, currRelPath, mqid, cliID, servID);
        }
        else
        {
            /* Don't send .mine's */
            ext = fileExt(dit->d_name);
            if (    0 == strcmp(".mine", ext) ||
                    0 == strcmp(".new", ext) )
                continue;


            if (snd_file_MQ (mqid, cliID, servID, currPath, 0) < 0)
            {
                fatal ("Error sending file\n");
            }
        }
    }

    /*
    * Close the stream. And check for errors. */
    if (closedir(dip) == -1)
    {
        perror("closedir");
        return 0;
    }

    return 1;
}


int srcvMQAns (char * msg)
{
	int ret = -1;
	if (msg != NULL)
		sscanf(msg, "%d", &ret);
	return ret;
}

int readNumMQ (char * msg, int * ret)
{
	int i = 0;
	if (msg != NULL)
	{
		while (msg[i] == ',')
			i++;
		sscanf(msg + i, "%d", ret);
		return i;
	}
	return -1;
}

int getNumMQ (char * msg, int * ret)
{
    int i = 0, j = 0;
    char aux[FILE_LEN] = {0};
    if (msg != NULL)
    {
        while (*msg == ',')
        {
            msg++;
            j++;
        }
        while(msg[i] != ',' && msg[i] != 0)
        {
            aux[i] = msg[i];
            i++;
        }

        if (msg[i] != 0)
            i++;

        sscanf(aux,"%d", ret);
        return i + j;
    }
    return -1;
}

int readParamMQ (char * msg, char * param)
{
    int i = 0;

    if(msg != NULL)
    {
        while (msg[i] == ',')
            i++;

        while (msg[i] != ',' && msg[i] != 0)
        {
            param[i] = msg[i];
            i++;
        }

        param[i] = 0;
        if(msg[i] != 0)
            i++;

        return i;
    }

    return -1;
}
